package com.edu.framework.websocket.handler;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.edu.common.utils.StringUtils;
import com.edu.common.utils.reflect.SpringReflectUtils;
import com.edu.framework.websocket.WebSocketHolder;
import com.edu.framework.websocket.message.base.NotFindResponse;
import com.edu.framework.websocket.message.base.Message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class WebSocketHandler extends AbstractWebSocketHandler implements InitializingBean {


    private ApplicationContext applicationContext;

    private final Map<String, IHandler> HANDLERS = new HashMap<>();

    private final Map<String, Class<? extends Message>> MESSAGE_CLASS = new HashMap<>();

    public WebSocketHandler(ApplicationContext context) {
        this.applicationContext = context;
    }

    @Override
    public void afterPropertiesSet() {
        applicationContext.getBeansOfType(IHandler.class).values()
                .forEach(messageHandler -> {
                    String type = messageHandler.getType();
                    if(HANDLERS.containsKey(type)){
                        throw new RuntimeException("duplicate type for : " +type);
                    }
                    HANDLERS.put(type, messageHandler);
                    Class classGenricType = SpringReflectUtils.getInterfaceFirstGenericType(messageHandler.getClass());
                    MESSAGE_CLASS.put(type, classGenricType);
                });
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String code = (String)session.getAttributes().get("code");
        if(StringUtils.isEmpty(code)){
            session.sendMessage(new TextMessage(" code must not empty!"));
            session.close();
        }
        WebSocketHolder.addSession(session,code);
    }



    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
        try {
            session.close(CloseStatus.NOT_ACCEPTABLE.withReason("Binary messages not supported"));
        } catch (IOException ignored) {
            log.error("handleBinaryMessage error:", ignored);
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        try {
            if (!session.isOpen()) {
                return;
            }
            JSONObject jsonMessage = JSON.parseObject(message.getPayload());
            String messageType = jsonMessage.getString("type");
            IHandler messageHandler = HANDLERS.get(messageType);
            if (messageHandler == null) {
                session.sendMessage(new TextMessage(JSONObject.toJSONString(NotFindResponse.build(messageType))));
                return;
            }
            Class<? extends Message> messageClass = MESSAGE_CLASS.get(messageType);
            if (messageClass == null) {
                return;
            }
            Message messageObj = JSON.parseObject(message.getPayload(), messageClass);
            if(log.isDebugEnabled()){
                log.debug("端上的请求：{}", JSONObject.toJSONString(messageObj));
            }
            messageHandler.execute(session, messageObj);
        } catch (Exception ignored) {
            log.error("handleTextMessage error:", ignored);
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        WebSocketHolder.removeSession(session);
    }
}
